console.log('你好')
function printName(obj:{first:string,last?:string}){
    if(obj.last !==undefined){
        console.log(obj.last.toUpperCase())
    }
    //或者
    console.log(obj.last?.toUpperCase())
    return `${obj.first}-${obj.last}`
}
let username = printName({
    first: 'lai'
})
console.log(username)

let changeingString = 'hello world'
changeingString = 'Olá Mundo'
// changeingString
const constantString = 'hello world'
// constantString
declare function handleRequest(url:string, method:'GET'|'POST'):void

// const req = {url: 'https://example.com', method: 'GET'}
// handleRequest(req.url, req.method as 'GET')

const req = {url: 'https://example.com', method: 'GET'} as const  //as const 将整个对象转换为类型字面，不可更改
handleRequest(req.url, req.method)

function doSomething(x:string|null){
    if(x===null){
        //TODO: do nothing
    }else{
        console.log('hello, '+x.toLowerCase())
    }
}
//非空断言运算符（后缀！）
function liveDangerously(x?:number|null){
    console.log(x!.toFixed())
}

function printAll(str:string|string[]|null){
    if(typeof str === "object"&&str){
        for(const s of str){
            console.log(s)
        }
    }
    else if(typeof str === 'string'){
        console.log(str)
    }
    else{
        //TODO: do something
    }
}

function geeter(fn: (a:string)=>void){
    fn("hello, world")
}
function printToConsole(s:string){
    console.log(s)
}
geeter(printToConsole)
//调用签名
type DescFn = {
    description: string,
    (someArg:number):boolean
}
function doSomethingFn(fn: DescFn){
    console.log(fn.description + ' returned '+ fn(6))
}
function Func(someArg: number){
    return someArg>3
}
Func.description = 'default description'
console.log(typeof Func)  //funtion
doSomethingFn(Func)

//构造签名
type someConstructor = {
    new (s:string):string
}
function fnConstruct(ctor:someConstructor){
    return new ctor('thc')
}
interface CallOrConstruct{
    new (s:string):Date
    (n?:number):string
}

function firstElement<Type>(arr:Type[]):Type|undefined{
    return arr[0]
}

function map<Input,Output>(arr:Input[],func:(arg:Input)=>Output):Output[]{
    return arr.map(func)
}
const parsed = map(['1','2','3'], n=>parseInt(n))

//约束条件
function longest<Type extends {length: number}>(a:Type,b:Type):Type{
    if(a.length>=b.length){
        return a
    }
    return b
}
// function minimumLength<Type extends {length: number}>(
//     obj:Type,
//     minimum: number
// ):Type{
//     if(obj.length>=minimum){
//         return obj
//     }
//     return {length: minimum}
// }

// 指定类型参数
function combine<T>(arr1:T[],arr2:T[]):T[]{
    return arr1.concat(arr2)
}
const arrs = combine<string|number>([1,2,3], ['hello']) //注意<string|number>的写法
//函数重载
function makeDate(timestamp:number):Date;
function makeDate(m:number,d:number,y:number):Date;
function makeDate(mOrTimestamp:number,d?:number,y?:number):Date{
    if(d!==undefined&&y!==undefined){
        return new Date(y,mOrTimestamp,d)
    }
    return new Date(mOrTimestamp)
}
const d1 = makeDate(15464)
const d2 = makeDate(2023,8,10)
// const d3 = makeDate(1,3)
function fail(msg:string):never{
    throw new Error(msg)
}

const args = [8,5]
// args.push(2)
// error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
// const angle = Math.atan2(...args)  
//可能考虑是args的长度会发生改变，此次atan2()的参数就不止2个了，所以报错，解决方案如下：

const argss = [8,5] as const //as const 相当于只读，不可更改
const angles = Math.atan2(...argss)

// 对象的类型注释遵循解构语法
type ABC = {a:number,b:number,c:number}
function sumFn({a,b,c}:ABC){
    console.log(a+b+c)
}

interface PainOptions{
    shap:string,
    xPos?:number,
    yPos?:number
}

function paintShap(opt:PainOptions){
    console.log('x coordinate at ', opt.xPos)
}
//设置默认值,避免出现opt.xPos = undefined的情况
function paintShaps({shap,xPos=0,yPos=0}:PainOptions){
    console.log('x coordinate at ',xPos)
}

// TypeScript 在检查两种类型是否兼容时不会考虑这两种类型的属性是否为 readonly，因此 readonly 属性也可以通过别名来更改。
interface Person_{
    name:string,
    age: number
}
interface ReadonlyPerson{
    readonly name:string,
    readonly age:number
}
let writablePerson:Person_ = {
    name: 'Hero thc',
    age: 28
}
let readonlyPerson:ReadonlyPerson = writablePerson
console.log(readonlyPerson.age) // 28
writablePerson.age++;
console.log(readonlyPerson.age) //29

// declare function getStringArray(): StringArray;

// ---cut---
interface StringArrays {
  [index: number]: string;
}
// const myArray: StringArrays = getStringArray();
// const secondItem = myArray[1];

//拓展类型
interface BasicAddress{
    name?:string,
    street:string,
    city:string,
    country:string,
    postalCode: string
}
interface AddressWithUnit extends BasicAddress{
    unit:string
}
// 交集类型（&） ：主要用于组合现有的对象类型
interface Colorful{
    color:string,
    name: string
}
interface Circle{
    radius: number,
    name:string
}
type ColorfulCircle = Colorful&Circle

let circleData:ColorfulCircle = {
    color: '#f00',
    radius: 20,
    name: '颜色'
}
function identity<T>(arg:T):T{
    return arg
}
let myIdentity: (<T>(arg:T)=>T) = identity

//将泛型类型写为对象字面量类型的调用签名
let myIdentity_: {
    <T>(arg:T):T
} = identity

//泛型类具有泛型接口相似的形状，泛型类在类名称后面的尖括号（<>）中有一个泛型类型参数列表
class GenericNumber<NumType>{
    zeroValue:NumType
    add: (x:NumType,y:NumType)=>NumType
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = (x,y)=>{
    return x+y
}
//在泛型约束中使用类型参数
function getProperty<T, Key extends keyof T>(obj:T, key:Key){
    return obj[key]
}
let xz = {a:1,b:2,c:3,d:4}
getProperty(xz,'a')
// getProperty(xz, 'm')

//keyof用法
interface IPerson{
    name: string,
    age: number,
    sex:0|1
}
type p1 = keyof IPerson //相当于p1的值为: name|age|sex
interface ITset{
    key: p1
}
let test:ITset = {
    key: 'sex'
}
class GoodGreeter{
    name: string;
    constructor(){
        this.name = 'hello'
    }
}
// class OKGreeter{
//     name!:string
// }
// let okgreeter = new OKGreeter()
// okgreeter.name = '2'


//implements从句
interface Pingable{
    ping():void
}
class Sonar implements Pingable{
    ping(){
        console.log('ping!')
    }
}
// class Ball implements Pingable{  
//     pong(){
//         console.log('pong')
//     }
// }

interface Checkable{
    check(name:string):boolean
}
class NameChecker implements Checkable{
    //name必须指定类型，否则会报错
    check(name:string):boolean{
        return name.toLowerCase() === 'ok'
    }
}

interface A{
    x:number
    y?:number
}
class C implements A{
    x=0
}
const c = new C()
// c.y = 0
c.x = 0

//extends 从句
class Animals{
    move(){
        console.log('Moving along')
    }
}
class Dogs extends Animals{
    woof(times:number){
        for(let i=0; i<times;i++){
            console.log('woofs')
        }
    }
}
const d = new Dogs()
d.move()
d.woof(3)
//覆盖方法
class Base{
    greet(){
        console.log('你好，哈哈！')
    }
}
class Derived extends Base{
    greet(name?:string): void {
       if(name === undefined) {
        super.greet()
       }
       else{
        console.log('你好吗，很高兴认识你！')
       }
    }
}
const dd = new Derived()
dd.greet()
dd.greet('thc')

interface Animales{
    dateOfBirth: any
}
interface Doges extends Animales{
    breed:any
}
class AnimalHouse{
    resident: Animales
    constructor(animal:Animales){
        this.resident = animal
    }
}
class DogHouse extends AnimalHouse{
    declare resident: Doges
    constructor(dog:Doges){
        super(dog)
    }
}
class catGod {
    name:string
    constructor(name:string){
        this.name = name
    }
}
let agCat = new catGod('cat')

abstract class AbstractParent{
    abstract abstractFn():string
}

class parents{
    func():string{
        return '普通类'
    }
}

class child extends AbstractParent{
    abstractFn(): string {
        return '实现抽象函数'
    }
}
class child1 extends parents{
    func2(): string {
        return ''
    }
}
abstract class child3 extends parents{
    abstract func3():string
    func3Real():string{
        return ''
    }
}
abstract class child4 extends AbstractParent{
    abstract func4():string
    func4Real():string{
        return ''
    }
}
interface IExample extends AbstractParent{
    name:string
    age:number
}
interface Chrome extends parents{
    name: string
}

//extends 继承、拓展

// implements 实现
class example implements Chrome{
    name:string = 'thc'
    func():string{
        return ''
    }
}
let ass = new example()
function chromeFn():Chrome{
    return {
        name: 'e',
        func: ():string=>{return ''}
    }
}
class MsgError extends Error{
    constructor(m:string){
        super(m)
    }
    sayError(){
        return '错了，错在'+this.message
    }
}

class Greeter{
    protected m = 10
    public greet(){
        console.log('protected', this.getName())
    }
    protected getName(){
        return 'sb'
    }
}
class SpecialGreeter extends Greeter{
    m = 12
    public Howdy(){
        console.log('Howdy ', this.getName())
    }
}
const gee = new SpecialGreeter()
gee.greet()
gee.Howdy()
// gee.getName()  //protected修饰，不可直接调用

class MySafe{
    private secretKey:string = '123456'  
}
const s = new MySafe()
// console.log(s.secretKey)  //private属性， 只能在类中访问secretKey
console.log(s['secretKey'])

// class Box<T>{
//     static defaultValue:T
// }
